use common::Server;
use ctapi::core::entity::jump_url::JumpUrl;
use ctapi::core::entity::resource_url::ResourceUrl;
use ctapi::core::entity::whitelist_key::WhitelistKey;
use ctapi::core::model::resource_version_entry::ResourceVersionEntry;
use ctapi::router::channel_setting_router::rsp::GetChannelInfoResponse;
use ctapi::router::common_router::rsp::{GetInnerIpResponse, GetUpdateInfoResponse};
use reqwest::{Request, Response};
use rstest::{fixture, rstest};
use serde_json::json;
use share::core::channel::Channel;
use share::core::device_id::DeviceId;
use share::core::native_version::NativeVersion;
use share::core::resource_version::ResourceVersion;
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr};
use std::str::FromStr;
use std::sync::Arc;
use std::{future, string};
use tokio::sync::Mutex;
use uuid::Uuid;
use warp::body::json;

mod common;

#[fixture]
async fn shared_server() -> Arc<Mutex<Server>> {
    common::shared_server().await
}

/// 测试服务器心跳
#[tokio::test]
#[rstest]
async fn test_ping(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let result = shared_server.lock().await.get("/ping").await;

    assert_eq!(result, "pong");
}

/// 测试获取的IP地址
#[tokio::test]
#[rstest]
async fn test_get_ip(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let result = shared_server.lock().await.get("/ip").await;

    assert_eq!(result, r#"127.0.0.1"#);
}

/// 测试记录内网IP地址
#[tokio::test]
#[rstest]
#[case("1.2.3.4")]
async fn test_set_inner_ip(#[future] shared_server: Arc<Mutex<Server>>, #[case] ip: String) {
    let shared_server = shared_server.await;
    let ip = IpAddr::from_str(&ip).unwrap();

    let server_guard = shared_server.lock().await;
    let client = reqwest::Client::new();
    let set_response = client
        .post(server_guard.url("/inner_ip"))
        .header("X-Real-Ip", ip.to_string())
        .send()
        .await
        .unwrap();

    let get_response = server_guard.get("/inner_ip").await;
    let get_response: GetInnerIpResponse = serde_json::from_str(&get_response).unwrap();

    assert_eq!(set_response.status(), reqwest::StatusCode::OK);
    assert_eq!(get_response.ip, Some(ip))
}

/// 测试删除记录的内网IP地址
#[tokio::test]
#[rstest]
async fn test_delete_inner_ip(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let ip = IpAddr::from_str("3.3.3.3").unwrap();

    let mut server_guard = shared_server.lock().await;
    let client = reqwest::Client::new();
    let set_response = client
        .post(server_guard.url("/inner_ip"))
        .header("X-Real-Ip", ip.to_string())
        .send()
        .await
        .unwrap();
    server_guard.delete("/inner_ip").await;

    let get_response = server_guard.get("/inner_ip").await;
    let get_response: GetInnerIpResponse = serde_json::from_str(&get_response).unwrap();

    assert_eq!(set_response.status(), reqwest::StatusCode::OK);
    assert_eq!(get_response.ip, None)
}

/// 测试获取渠道详情
///法与生产环境API返回的渠道信息是否一致。
/// 通过参数化测试覆盖6个主要渠道，包括国际版和大陆特供版渠道。
///
/// 覆盖渠道说明：
/// - 国际版渠道：QooApp/GooglePlay/AppStore
/// - 大陆特供版渠道：TapTapMainland/BiliBiliM
// /// 该测试验证本地服务器的 `fetch_channel_info` 方ainland/AppStoreMainland
#[tokio::test]
#[rstest]
#[case("QooApp")]
#[case("GooglePlay")]
#[case("AppStore")]
#[case("TapTapMainland")]
#[case("BiliBiliMainland")]
#[case("AppStoreMainland")]
async fn test_default_channel_info(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] channel: String,
) {
    let shared_server = shared_server.await;

    let channel = Channel::from_str(&channel).unwrap();
    let api_domain = channel.get_api_domain();

    let expect: GetChannelInfoResponse = reqwest::get(format!(
        "https://{}/channel_setting/info/{}",
        api_domain, channel
    ))
    .await
    .unwrap()
    .text()
    .await
    .map(|s| serde_json::from_str(&s).unwrap())
    .unwrap();

    let channel_info = shared_server.lock().await.fetch_channel_info(channel).await;

    let native_version: Vec<NativeVersion> =
        channel_info.hot_update_settings.keys().cloned().collect();

    assert_eq!(channel_info.name, expect.name);
    assert_eq!(
        channel_info.latest_native_version,
        expect.latest_native_version
    );
    assert_eq!(channel_info.min_native_version, expect.min_native_version);
    assert_eq!(channel_info.jump_url, expect.jump_url);

    for nv in native_version {
        let hot_update_setting = channel_info.hot_update_settings.get(&nv).unwrap();
        let expected = expect.hot_update_settings.get(&nv).unwrap();

        assert_eq!(hot_update_setting.resource_url, expected.resource_url);
        assert_eq!(
            hot_update_setting.resource_version,
            expected.resource_version
        );
    }
}

/// 测试设置渠道原生版本号的接口
///
/// 该测试验证通过 `/channel_setting/native_version` 接口设置渠道的
/// 最低(min)和最新(latest)原生版本号的功能是否正确。
///
/// 测试场景覆盖：
/// - 全零版本号(0,0) - 边界值测试
/// - 最小版本差(0,1) - 测试版本递增基础场景
/// - 相同版本(1,1) - 测试最小=最新版本的特殊情况  
/// - 正常版本差(3,6) - 测试典型多版本跨度场景
///
/// 测试方法：
/// 1. 构造包含测试版本号的JSON请求体
/// 2. 调用PUT接口更新ForceLogin渠道的版本设置
/// 3. 重新获取渠道信息验证版本号是否更新成功
///
/// 特别注意：
/// - 使用ForceLogin特殊渠道进行测试，避免影响其他测试
/// - 验证min_native_version和latest_native_version双字段
/// - 包含版本号相等和不相等的多种情况
#[tokio::test]
#[rstest]
#[case(0, 0)]
#[case(0, 1)]
#[case(1, 1)]
#[case(3, 6)]
async fn test_set_native_version(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] min_native_version: u32,
    #[case] latest_native_version: u32,
) {
    let min_native_version = NativeVersion::new(min_native_version).unwrap();
    let latest_native_version = NativeVersion::new(latest_native_version).unwrap();
    let shared_server = shared_server.await;

    let mut lock = shared_server.lock().await;

    let json = format!(
        r#"
        {{
            "channel":"ForceLogin",
            "min_native_version":{min_native_version},
            "latest_native_version":{latest_native_version}
        }}
        "#,
    );
    lock.put_json("/channel_setting/native_version", json).await;

    let channel_info = lock.fetch_channel_info(Channel::ForceLogin).await;
    drop(lock);
    assert_eq!(channel_info.min_native_version, min_native_version);
    assert_eq!(channel_info.latest_native_version, latest_native_version);
}

/// 测试设置渠道跳转URL的接口功能
///
/// 该测试验证通过 `/channel_setting/jump_url` 接口更新渠道跳转URL的功能。
/// 使用ForceLogin特殊渠道进行测试，避免影响其他测试用例。
///
/// 测试要点：
/// 1. 验证PUT接口能够正确接收并处理跳转URL参数
/// 2. 验证更新后的渠道信息能正确返回新设置的URL
/// 3. 使用真实URL格式进行测试（本例使用Bing首页URL）
///
/// 测试流程：
/// 1. 构造包含测试URL的JSON请求体
/// 2. 调用PUT接口更新ForceLogin渠道的jump_url
/// 3. 重新获取渠道信息验证URL是否更新成功
///
/// 注意：
/// - 使用明确的mut标识表示需要修改服务器状态
/// - 及时释放服务器锁(通过drop或作用域结束)
/// - 使用真实有效的URL格式测试(http/https协议头)
#[tokio::test]
#[rstest]
#[case("https://www.bing.com/")]
async fn test_set_jump_url(#[future] shared_server: Arc<Mutex<Server>>, #[case] url: String) {
    let url = JumpUrl::new(url).unwrap();

    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let json = format!(
        r#"
        {{
            "channel":"ForceLogin",
            "jump_url":"{}"
        }}
        "#,
        url
    );
    server_guard
        .put_json("/channel_setting/jump_url", json)
        .await;

    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    assert_eq!(channel_info.jump_url, url);
}

/// 测试添加原生版本热更新配置的功能
///
/// 该测试验证通过 `/hot_update_setting` 接口为指定原生版本添加热更新配置的功能。
/// 测试流程会先清理现有非零版本配置，然后添加新配置并验证所有字段。
///
/// 测试场景：
/// - 原生版本号(native_version)管理
/// - 资源版本(raw/default)设置
/// - 资源URL配置
/// - 版本注释信息存储
///
/// 测试步骤：
/// 1. 获取ForceLogin渠道现有配置
/// 2. 清理所有非零版本的热更新配置（保留基准配置）
/// 3. 添加包含完整信息的新版本配置
/// 4. 验证所有字段是否正确存储
///
/// 参数说明：
/// - native_version: 要添加的原生版本号
/// - raw_version: 原始资源版本标识
/// - default_version: 默认资源版本标识  
/// - comment: 版本注释信息
/// - resource_url: 资源下载URL
///
/// 注意：
/// - 使用ForceLogin渠道避免影响其他测试
/// - 保留0版本作为基准配置
/// - 验证所有输入字段的完整性
#[tokio::test]
#[rstest]
#[case("abc", "def", "Hello world", "https://www.bing.com")]
async fn test_add_native_version(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw_version: String,
    #[case] default_version: String,
    #[case] comment: String,
    #[case] resource_url: String,
) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let max: u32 = old
        .hot_update_settings
        .keys()
        .max()
        .cloned()
        .unwrap_or_default()
        .into();

    let native_version = NativeVersion::new(max + 1).unwrap();
    let raw_version = ResourceVersion::new(raw_version).unwrap();
    let default_version = ResourceVersion::new(default_version).unwrap();
    let resource_url = ResourceUrl::new(resource_url).unwrap();

    let json = format!(
        r#"
        {{
            "channel":"ForceLogin",
            "native_version":{native_version},
            "resource_version":{{
                "raw":"{raw_version}",
                "default":"{default_version}",
                "comment":"{comment}"
            }},
            "resource_url":"{resource_url}"
        }}
        "#
    );
    server_guard.post_json("/hot_update_setting", json).await;
    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;

    let info = channel_info
        .hot_update_settings
        .get(&native_version)
        .unwrap();

    assert_eq!(info.resource_version.raw, raw_version);
    assert_eq!(info.resource_version.default, default_version);
    assert_eq!(info.resource_version.comment, comment);
    assert_eq!(info.resource_url, resource_url);
}

/// 测试设置基准版本(0版本)资源版本号的功能
///
/// 该测试验证通过 `/hot_update_setting/resource_version` 接口
/// 更新基准版本(0版本)资源版本配置的功能。
///
/// 测试重点：
/// 1. 验证原始资源版本号(raw)和默认资源版本号(default)的更新
/// 2. 确保基准版本(0版本)的特殊处理逻辑
/// 3. 保持注释字段(comment)为空的可选参数处理
///
/// 测试流程：
/// 1. 构造包含测试版本号的JSON请求体
/// 2. 通过PUT接口更新ForceLogin渠道的0版本资源配置
/// 3. 验证返回的资源配置是否与输入一致
///
/// 参数说明：
/// - raw_resource_version: 原始资源版本标识（通常为内部版本号）
/// - default_resource_version: 默认资源版本标识（用户可见版本号）
///
/// 注意：
/// - 专门测试0版本的特殊处理逻辑
/// - 使用ForceLogin渠道避免污染其他测试数据
/// - 保持comment字段为空的测试边界情况
#[tokio::test]
#[rstest]
#[case("abc", "def")]
async fn test_set_resource_version(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw_resource_version: String,
    #[case] default_resource_version: String,
) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;

    let native_version = old.hot_update_settings.keys().min().unwrap();
    let raw_resource_version = ResourceVersion::new(raw_resource_version).unwrap();
    let default_resource_version = ResourceVersion::new(default_resource_version).unwrap();
    let json = format!(
        r#"
        {{
            "channel":"ForceLogin",
            "native_version":{native_version},
            "resource_version":{{
                "raw":"{raw_resource_version}",
                "default":"{default_resource_version}",
                "comment":""
            }}
        }}
        "#
    );
    server_guard
        .put_json("/hot_update_setting/resource_version", json)
        .await;

    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let hot_update_info = channel_info
        .hot_update_settings
        .get(&native_version)
        .unwrap();
    assert_eq!(hot_update_info.resource_version.raw, raw_resource_version);
    assert_eq!(
        hot_update_info.resource_version.default,
        default_resource_version
    );
}

/// 测试设置基准版本(0版本)资源URL的功能
///
/// 该测试验证通过 `/hot_update_setting/resource_url` 接口更新基准版本(0版本)
/// 资源下载URL的功能。测试URL支持模板格式(包含{0}占位符)。
///
/// 测试要点：
/// 1. 验证资源URL的更新功能
/// 2. 确保URL模板格式的正确处理
/// 3. 基准版本(0版本)的特殊URL配置
///
/// 测试流程：
/// 1. 构造包含模板URL的JSON请求体
/// 2. 通过PUT接口更新ForceLogin渠道的0版本资源URL
/// 3. 验证返回的URL是否与设置值一致
///
/// 参数说明：
/// - url: 资源URL模板字符串(示例中使用Bing URL包含{0}版本占位符)
///
/// 注意：
/// - 专门测试0版本的资源URL配置
/// - 验证URL模板格式的支持情况
/// - 使用ForceLogin渠道保持测试隔离性
#[tokio::test]
#[rstest]
#[case("https://www.bing.com/{0}")]
async fn test_set_resource_url(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] resource_url: String,
) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;

    let native_version = old.hot_update_settings.keys().min().unwrap();
    let resource_url = ResourceUrl::new(resource_url).unwrap();

    let json = format!(
        r#"
        {{
            "channel":"ForceLogin",
            "native_version":{native_version},
            "url":"{resource_url}"
        }}
        "#,
    );
    server_guard
        .put_json("/hot_update_setting/resource_url", json)
        .await;

    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let hot_update_info = channel_info
        .hot_update_settings
        .get(&native_version)
        .unwrap();
    assert_eq!(hot_update_info.resource_url, resource_url);
}

/// 测试添加IP白名单
///
/// 这个会向ForceLogin渠道中的最低的母包版本中添加一个IP白名单，IP为本机。然后检测渠道信息中的白名单列表中确实存在，
/// 并且获取更新信息时会得到白名单对应的资源版本号。
#[tokio::test]
#[rstest]
#[case("abc", "def", "hello world")]
async fn test_add_whitelist_by_ip(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw: String,
    #[case] default: String,
    #[case] comment: String,
) {
    let shared_server = shared_server.await;
    let raw = raw.parse::<ResourceVersion>().unwrap();
    let default = default.parse::<ResourceVersion>().unwrap();

    let mut server_guard = shared_server.lock().await;
    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let native_version = old.hot_update_settings.keys().min().unwrap();

    server_guard
        .delete(format!(
            "/whitelist/clear/{}/{}",
            Channel::ForceLogin,
            native_version
        ))
        .await;

    let json = format!(
        r#"
        {{
            "channel": "ForceLogin",
            "native_version": {native_version},
            "key": "ip:127.0.0.1",
            "resource_version": {{
                "raw": "{raw}",
                "default": "{default}",
                "comment": "{comment}"
            }}
        }}
        "#
    );

    server_guard.put_json("/whitelist", json).await;

    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let hot_update_info = channel_info
        .hot_update_settings
        .get(native_version)
        .unwrap();
    let entry = hot_update_info.whitelist.values().next().unwrap();

    let update_info = server_guard
        .fetch_update_info(
            Channel::ForceLogin,
            native_version.clone(),
            DeviceId::default(),
        )
        .await;

    assert_eq!(entry.raw, raw);
    assert_eq!(entry.default, default);
    assert_eq!(entry.comment, comment);

    assert_eq!(update_info.raw_resource_version, Some(raw));
    assert_eq!(update_info.default_resource_version, Some(default));
}

/// 测试添加IP白名单，非白名单内的请求要获得正确的数据
///
/// 这个会向ForceLogin渠道中的最低的母包版本中添加一个IP白名单，IP为127.0.0.2。然后使用本机地址请求数据
#[tokio::test]
#[rstest]
#[case("abc", "def", "hello world")]
async fn test_add_whitelist_by_ip_other(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw: String,
    #[case] default: String,
    #[case] comment: String,
) {
    let shared_server = shared_server.await;
    let raw = raw.parse::<ResourceVersion>().unwrap();
    let default = default.parse::<ResourceVersion>().unwrap();

    let mut server_guard = shared_server.lock().await;
    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let native_version = old.hot_update_settings.keys().min().unwrap().clone();
    let old_entry = &old
        .hot_update_settings
        .get(&native_version)
        .unwrap()
        .resource_version;
    let old_raw = old_entry.raw.clone();
    let old_default = old_entry.default.clone();

    server_guard
        .delete(format!(
            "/whitelist/clear/{}/{}",
            Channel::ForceLogin,
            native_version
        ))
        .await;

    let json = format!(
        r#"
        {{
            "channel": "ForceLogin",
            "native_version": {native_version},
            "key": "ip:127.0.0.2",
            "resource_version": {{
                "raw": "{raw}",
                "default": "{default}",
                "comment": "{comment}"
            }}
        }}
        "#
    );

    server_guard.put_json("/whitelist", json).await;

    let update_info = server_guard
        .fetch_update_info(Channel::ForceLogin, native_version, DeviceId::default())
        .await;

    assert_eq!(update_info.raw_resource_version, Some(old_raw));
    assert_eq!(update_info.default_resource_version, Some(old_default));
}

/// 测试删除IP白名单
///
/// 这个测试会先添加一个IP白名单，然后删除该白名单。然后检测渠道信息中没有白名单，并且获取更新信息时不会受到白名单的影响
#[tokio::test]
#[rstest]
#[case("sdfgagw", "asdggg", "hello world")]
async fn test_remove_whitelist_by_ip(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw: String,
    #[case] default: String,
    #[case] comment: String,
) {
    let shared_server = shared_server.await;
    let raw = raw.parse::<ResourceVersion>().unwrap();
    let default = default.parse::<ResourceVersion>().unwrap();

    let mut server_guard = shared_server.lock().await;
    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let native_version = old.hot_update_settings.keys().min().unwrap();

    server_guard
        .delete(format!(
            "/whitelist/clear/{}/{}",
            Channel::ForceLogin,
            native_version
        ))
        .await;

    let json = format!(
        r#"
        {{
            "channel": "ForceLogin",
            "native_version": {native_version},
            "key": "ip:127.0.0.1",
            "resource_version": {{
                "raw": "{raw}",
                "default": "{default}",
                "comment": "{comment}"
            }}
        }}
        "#
    );

    server_guard.put_json("/whitelist", json).await;

    let delete_url = format!(
        "/whitelist/by/ip/{}/{}/127.0.0.1",
        Channel::ForceLogin,
        native_version
    );
    server_guard.delete(delete_url).await;

    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let hot_update_info = channel_info
        .hot_update_settings
        .get(native_version)
        .unwrap();

    let update_info = server_guard
        .fetch_update_info(
            Channel::ForceLogin,
            native_version.clone(),
            DeviceId::default(),
        )
        .await;

    assert!(hot_update_info.whitelist.values().next().is_none());

    assert_ne!(update_info.raw_resource_version, Some(raw));
    assert_ne!(update_info.default_resource_version, Some(default));
}

/// 测试添加设备白名单
///
/// 这个会向ForceLogin渠道中的最低的母包版本中添加一个设备白名单。然后检测渠道信息中的白名单列表中确实存在，
/// 并且获取更新信息时会得到白名单对应的资源版本号。
#[tokio::test]
#[rstest]
#[case("adsgih", "sgdghii", "hello world")]
async fn test_add_whitelist_by_device_id(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw: String,
    #[case] default: String,
    #[case] comment: String,
) {
    let shared_server = shared_server.await;
    let raw = raw.parse::<ResourceVersion>().unwrap();
    let default = default.parse::<ResourceVersion>().unwrap();
    let device_id = Uuid::new_v4();

    let mut server_guard = shared_server.lock().await;
    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let native_version = old.hot_update_settings.keys().min().unwrap();

    server_guard
        .delete(format!(
            "/whitelist/clear/{}/{}",
            Channel::ForceLogin,
            native_version
        ))
        .await;

    let json = format!(
        r#"
        {{
            "channel": "ForceLogin",
            "native_version": {native_version},
            "key": "device_id:{device_id}",
            "resource_version": {{
                "raw": "{raw}",
                "default": "{default}",
                "comment": "{comment}"
            }}
        }}
        "#
    );

    server_guard.put_json("/whitelist", json).await;

    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let hot_update_info = channel_info
        .hot_update_settings
        .get(native_version)
        .unwrap();
    let entry = hot_update_info.whitelist.values().next().unwrap();

    let update_info = server_guard
        .fetch_update_info(
            Channel::ForceLogin,
            native_version.clone(),
            device_id.into(),
        )
        .await;

    assert_eq!(entry.raw, raw);
    assert_eq!(entry.default, default);
    assert_eq!(entry.comment, comment);

    assert_eq!(update_info.raw_resource_version, Some(raw));
    assert_eq!(update_info.default_resource_version, Some(default));
}

/// 测试添加设备白名单，不再白名单的请求应该得到正确的数据
///
/// 这个会向ForceLogin渠道中的最低的母包版本中添加一个设备白名单。然后通过另一个设备对请求数据，检测另一个设备请求
/// 的结果是否正确
#[tokio::test]
#[rstest]
#[case("adsgih", "sgdghii", "hello world")]
async fn test_add_whitelist_by_device_id_other(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw: String,
    #[case] default: String,
    #[case] comment: String,
) {
    let shared_server = shared_server.await;
    let raw = raw.parse::<ResourceVersion>().unwrap();
    let default = default.parse::<ResourceVersion>().unwrap();
    let device_id = Uuid::new_v4();
    let another_device_id = Uuid::new_v4();

    let mut server_guard = shared_server.lock().await;
    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let native_version = old.hot_update_settings.keys().min().unwrap().clone();
    let old_entry = &old
        .hot_update_settings
        .get(&native_version)
        .unwrap()
        .resource_version;
    let old_raw = old_entry.raw.clone();
    let old_default = old_entry.default.clone();

    server_guard
        .delete(format!(
            "/whitelist/clear/{}/{}",
            Channel::ForceLogin,
            native_version
        ))
        .await;

    let json = format!(
        r#"
        {{
            "channel": "ForceLogin",
            "native_version": {native_version},
            "key": "device_id:{device_id}",
            "resource_version": {{
                "raw": "{raw}",
                "default": "{default}",
                "comment": "{comment}"
            }}
        }}
        "#
    );

    server_guard.put_json("/whitelist", json).await;

    let update_info = server_guard
        .fetch_update_info(
            Channel::ForceLogin,
            native_version,
            another_device_id.into(),
        )
        .await;

    assert_eq!(update_info.raw_resource_version, Some(old_raw));
    assert_eq!(update_info.default_resource_version, Some(old_default));
}

/// 测试添加多个白名单
///
/// 先清理强登渠道的某个母包版本的所有白名单，再添加两个白名单，一个是关于IP的，一个是关于设备ID的。然后分别在两种
/// 情况下请求更新信息。检测更新信息中的资源版本号是否正确
#[tokio::test]
#[rstest]
async fn test_add_many_whitelist(#[future] shared_server: Arc<Mutex<Server>>) {
    let shared_server = shared_server.await;
    let mut server_guard = shared_server.lock().await;

    let channel = Channel::ForceLogin;
    let channel_info = server_guard.fetch_channel_info(channel).await;
    let native_version = channel_info.hot_update_settings.keys().min().unwrap();

    let ip = IpAddr::from_str("1.2.3.4").unwrap();
    let entry0 = ResourceVersionEntry {
        raw: "abc".parse().unwrap(),
        default: "def".parse().unwrap(),
        comment: "hello".parse().unwrap(),
    };
    let device_id = DeviceId::new(&Uuid::new_v4().to_string()).unwrap();
    let entry1 = ResourceVersionEntry {
        raw: "aaa".parse().unwrap(),
        default: "bbb".parse().unwrap(),
        comment: "world".parse().unwrap(),
    };

    let entries: HashMap<WhitelistKey, ResourceVersionEntry> = vec![
        (WhitelistKey::Ip(ip.clone()), entry0.clone()),
        (WhitelistKey::DeviceId(device_id.clone()), entry1.clone()),
    ]
    .into_iter()
    .collect();

    let json = json!({
        "channel": channel,
        "native_version": native_version,
        "entries": entries
    });

    server_guard
        .delete(format!("/whitelist/clear/{channel}/{native_version}"))
        .await;
    server_guard
        .post_json("/whitelist/many", json.to_string())
        .await;

    let client = reqwest::Client::new();
    let rsp0 = client
        .get(server_guard.url(&format!(
            "/update_info?channel={}&version={}&device_id=xxxx",
            channel, native_version
        )))
        .header("X-Real-Ip", ip.to_string())
        .send()
        .await
        .unwrap();

    assert_eq!(rsp0.status(), reqwest::StatusCode::OK);
    let info0 = rsp0.text().await.unwrap();
    let info0: GetUpdateInfoResponse = serde_json::from_str(&info0).unwrap();

    let rsp1 = client
        .get(server_guard.url(&format!(
            "/update_info?channel={}&version={}&device_id={}",
            channel, native_version, device_id
        )))
        .send()
        .await
        .unwrap();

    assert_eq!(rsp1.status(), reqwest::StatusCode::OK);
    let info1 = rsp1.text().await.unwrap();
    let info1: GetUpdateInfoResponse = serde_json::from_str(&info1).unwrap();

    assert_eq!(info0.raw_resource_version, Some(entry0.raw));
    assert_eq!(info0.default_resource_version, Some(entry0.default));

    assert_eq!(info1.raw_resource_version, Some(entry1.raw));
    assert_eq!(info1.default_resource_version, Some(entry1.default));
}

/// 测试删除IP白名单
///
/// 这个测试会先添加一个IP白名单，然后删除该白名单。然后检测渠道信息中没有白名单，并且获取更新信息时不会受到白名单的影响
#[tokio::test]
#[rstest]
#[case("ggiigsad", "goohgihiooh", "hello world")]
async fn test_remove_whitelist_by_device_id(
    #[future] shared_server: Arc<Mutex<Server>>,
    #[case] raw: String,
    #[case] default: String,
    #[case] comment: String,
) {
    let shared_server = shared_server.await;
    let raw = raw.parse::<ResourceVersion>().unwrap();
    let default = default.parse::<ResourceVersion>().unwrap();
    let device_id = Uuid::new_v4();

    let mut server_guard = shared_server.lock().await;
    let old = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let native_version = old.hot_update_settings.keys().min().unwrap();

    server_guard
        .delete(format!(
            "/whitelist/clear/{}/{}",
            Channel::ForceLogin,
            native_version
        ))
        .await;

    let json = format!(
        r#"
        {{
            "channel": "ForceLogin",
            "native_version": {native_version},
            "key": "device_id:{device_id}",
            "resource_version": {{
                "raw": "{raw}",
                "default": "{default}",
                "comment": "{comment}"
            }}
        }}
        "#
    );

    server_guard.put_json("/whitelist", json).await;

    let delete_url = format!(
        "/whitelist/by/device_id/{}/{}/{}",
        Channel::ForceLogin,
        native_version,
        device_id
    );
    server_guard.delete(delete_url).await;

    let channel_info = server_guard.fetch_channel_info(Channel::ForceLogin).await;
    let hot_update_info = channel_info
        .hot_update_settings
        .get(native_version)
        .unwrap();

    let update_info = server_guard
        .fetch_update_info(
            Channel::ForceLogin,
            native_version.clone(),
            device_id.into(),
        )
        .await;

    assert!(hot_update_info.whitelist.values().next().is_none());

    assert_ne!(update_info.raw_resource_version, Some(raw));
    assert_ne!(update_info.default_resource_version, Some(default));
}
